#define DOHOME_LOG_LVL          DOHOME_LOG_LVL_DEBUG
#define DOHOME_LOG_TAG          "hal_pwr_m"

#include "dohome_hal_pwr_measure.h"

#include <stdio.h>
#include <string.h>
#include <math.h>

#include <dohome_api.h>

DOHOME_CHAR_T _pwr_measure_fault_cls = 0;
dohome_pwr_measure_fault_cfg_t _pwr_measure_fault_cfg = {0};
dohome_pwr_measure_fault_cb_t _pwr_measure_fault_cb = NULL;
dohome_pwr_measure_type_t _pwr_measure_type = DOHOME_PWR_MEASURE_TYPE_MAX;

/************************************************************************
 * DOHOME_PWR_MEASURE_TYPE_HLW8012  AND  DOHOME_PWR_MEASURE_TYPE_BL0937 *
 ************************************************************************/

// Internal voltage reference value
#define V_REF_HLW               2.43
#define V_REF_BL0               1.218

// The factor of a 1mOhm resistor
// as per recomended circuit in datasheet
// A 1mOhm resistor allows a ~30A max measurement
#define R_CURRENT           (0.001)

// This is the factor of a voltage divider of 6x 470K upstream and 1k downstream
// Sonoff Pow has 5x 470K
// Smart DGM outlet has 3x 680K
// as per recomended circuit in datasheet
#define R_VOLTAGE_HLW           ((5 * 470) + 1) //2821 //2350
#define R_VOLTAGE_BL0        ((3 * 680) + 1) //1980 +1

// Frequency of the HLW8012 internal clock
#define F_OSC_HLW           (3579000)
#define F_OSC_BL0           (2000000)

// Maximum pulse with in microseconds
// If longer than this pulse width is reset to 0
// This value is purely experimental.
// Higher values allow for a better precission but reduce sampling rate
// and response speed to change
// Lower values increase sampling rate but reduce precission
// Values below 0.5s are not recommended since current and voltage output
// will have no time to stabilise
#define PULSE_TIMEOUT       (2000000l)

// CF1 mode
typedef enum {
    MODE_CURRENT = 0,
    MODE_VOLTAGE
} hlw8012_mode_t;


DOHOME_UINT32_T _power_energy_compensate = 0;

uint8_t _cf_pin;
uint8_t _cf1_pin;
uint8_t _sel_pin;

float _current_resistor = R_CURRENT;
float _voltage_resistor = R_VOLTAGE_HLW;
float _vref = V_REF_HLW;

float _current_multiplier; // Unit: us/A
float _voltage_multiplier; // Unit: us/V
float _power_multiplier;   // Unit: us/W

typedef struct 
{
    float voltage;
    float current;
    float power;
}doit_measure_multiplier_cal;

uint32_t _pulse_timeout = PULSE_TIMEOUT;    //Unit: us
volatile uint32_t _voltage_pulse_width = 0; //Unit: us
volatile uint32_t _current_pulse_width = 0; //Unit: us
volatile uint32_t _power_pulse_width = 0;   //Unit: us
volatile uint32_t _pulse_count = 0;

float _current = 0;
uint16_t _voltage = 0;
uint16_t _power = 0;

uint8_t _current_mode = 1;
uint8_t _model = 0;
volatile uint8_t _mode;

volatile uint64_t _last_cf_interrupt = 0;
volatile uint64_t _last_cf1_interrupt = 0;
volatile uint64_t _first_cf1_interrupt = 0;

uint8_t _correcting = 0;
uint8_t _has_correct = 0;


hlw8012_mode_t HLW8012_toggleMode();
uint16_t HLW8012_getActivePower(); 

void HLW8012_cf_interrupt(void *arg) {

    
}

void HLW8012_cf1_interrupt(void *arg) {

    
}

void HLW8012_checkCFSignal() {
    
}

void HLW8012_checkCF1Signal() {
    
}

// These are the multipliers for current, voltage and power as per datasheet
// These values divided by output period (in useconds) give the actual value
// For power a frequency of 1Hz means around 12W
// For current a frequency of 1Hz means around 15mA
// For voltage a frequency of 1Hz means around 0.5V
void _calculateDefaultMultipliers() {
    

}

float HLW8012_getCurrentMultiplier() { return _current_multiplier; };
float HLW8012_getVoltageMultiplier() { return _voltage_multiplier; };
float HLW8012_getPowerMultiplier() { return _power_multiplier; };

void HLW8012_setCurrentMultiplier(float current_multiplier) { _current_multiplier = current_multiplier; };
void HLW8012_setVoltageMultiplier(float voltage_multiplier) { _voltage_multiplier = voltage_multiplier; };
void HLW8012_setPowerMultiplier(float power_multiplier) { _power_multiplier = power_multiplier; };


static void fault_check(void *arg){

    
}


void HLW8012_init(uint8_t cf_pin, uint8_t cf1_pin, uint8_t sel_pin, uint8_t currentWhen, uint8_t model){
    
    
}

void HLW8012_setMode(hlw8012_mode_t mode) {
    
   
}

hlw8012_mode_t HLW8012_getMode() {
    return (_mode == _current_mode) ? MODE_CURRENT : MODE_VOLTAGE;
}

hlw8012_mode_t HLW8012_toggleMode() {
    hlw8012_mode_t new_mode = HLW8012_getMode() == MODE_CURRENT ? MODE_VOLTAGE : MODE_CURRENT;
    HLW8012_setMode(new_mode);
    return new_mode;
}

float HLW8012_getCurrent() {


    _current = (_current_pulse_width > 0) ? _current_multiplier / _current_pulse_width  : 0;
    return _current;

}

uint16_t HLW8012_getVoltage() {
    
    _voltage = (_voltage_pulse_width > 0) ? _voltage_multiplier / _voltage_pulse_width : 0;
    return _voltage;
}

uint32_t HLW8012_getEnergy() {

    /*
    Pulse count is directly proportional to energy:
    P = m*f (m=power multiplier, f = Frequency)
    f = N/t (N=pulse count, t = time)
    E = P*t = m*N  (E=energy)
    */
    return _pulse_count * _power_multiplier / 1000000l;
}

uint16_t HLW8012_getActivePower() {


    _power = (_power_pulse_width > 0) ? _power_multiplier / _power_pulse_width : 0;
    return _power;
}

uint16_t HLW8012_getApparentPower() {
    return 0;
}


float HLW8012_getPowerFactor() {
    return 0;
}

void HLW8012_resetEnergy() {
    _pulse_count = 0;
}

void HLW8012_expectedCurrent(float value) {
    // if (_current == 0) 
    HLW8012_getCurrent();
    if (_current > 0) _current_multiplier *= (value / _current);
}

void HLW8012_expectedVoltage(uint16_t value) {
    // if (_voltage == 0) 
    HLW8012_getVoltage();
    if (_voltage > 0) _voltage_multiplier *= ((float) value / _voltage);
}

void HLW8012_expectedActivePower(uint16_t value) {
    // if (_power == 0) 
    HLW8012_getActivePower();
    if (_power > 0) _power_multiplier *= ((float) value / _power);
}

void HLW8012_resetMultipliers() {
    _calculateDefaultMultipliers();
}

void HLW8012_setResistors(float current, float voltage_upstream, float voltage_downstream) {
    
}


dohome_op_ret dohome_hal_pwr_measure_init(dohome_pwr_measure_cfg_t cfg, dohome_pwr_measure_fault_cb_t fault_cb){

    return OPRT_OK;
}

dohome_op_ret dohome_hal_pwr_measure_set_default_voltage_cal(DOHOME_FLOAT_T voltage){
    if(!_has_correct){
        _voltage_multiplier = voltage;
    }
    return OPRT_OK;
}

dohome_op_ret dohome_hal_pwr_measure_set_default_current_cal(DOHOME_FLOAT_T current){
    if(!_has_correct){
        _current_multiplier = current;
    }
    return OPRT_OK;
}

dohome_op_ret dohome_hal_pwr_measure_set_default_power_cal(DOHOME_FLOAT_T power){
    if(!_has_correct){
        _power_multiplier = power;
    }
    return OPRT_OK;
}

dohome_op_ret dohome_hal_pwr_measure_set_fault_cfg(dohome_pwr_measure_fault_cfg_t fault_cfg){
    _pwr_measure_fault_cls = 1;
    _pwr_measure_fault_cfg = fault_cfg;
    return OPRT_OK;
}

dohome_op_ret dohome_hal_pwr_measure_get_fault_cfg(dohome_pwr_measure_fault_cfg_t *fault_cfg){
    memcpy(fault_cfg, &_pwr_measure_fault_cfg, sizeof(dohome_pwr_measure_fault_cfg_t));
    return OPRT_OK;
}

DOHOME_UINT16_T dohome_hal_pwr_measure_get_voltage_V(void){
    
    DOHOME_UINT16_T voltage = 0;

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        voltage = HLW8012_getVoltage();
        break;
    default:
        break;
    }

    return voltage;
}

DOHOME_UINT16_T dohome_hal_pwr_measure_get_current_mA(void){
    
    DOHOME_UINT16_T current = 0;
    DOHOME_FLOAT_T current_float = 0.0;

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        current_float = HLW8012_getCurrent();
        current = current_float*1000;
        break;
    default:
        break;
    }

    return current;
}

DOHOME_UINT16_T dohome_hal_pwr_measure_get_active_power_W(void){
    
    DOHOME_UINT16_T active_power = 0;

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        active_power = HLW8012_getActivePower();
        break;
    default:
        break;
    }

    return active_power;
}

DOHOME_UINT16_T dohome_hal_pwr_measure_get_power_energy_Wh(void){

    DOHOME_UINT32_T power_energy_Ws = 0;
    DOHOME_UINT16_T power_energy_Wh = 0;

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        power_energy_Ws = dohome_hal_pwr_measure_get_power_energy_Ws();
        power_energy_Wh = power_energy_Ws/(3600);
        break;
    default:
        break;
    }

    return power_energy_Wh;
}


DOHOME_UINT32_T dohome_hal_pwr_measure_get_power_energy_Ws(void){

    DOHOME_UINT32_T power_energy_Ws = 0;

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        power_energy_Ws = HLW8012_getEnergy();
        power_energy_Ws += _power_energy_compensate;
        break;
    default:
        break;
    }

    return power_energy_Ws;
}

void dohome_hal_pwr_measure_set_power_energy_Ws(DOHOME_UINT32_T Ws){

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        HLW8012_resetEnergy();
        _power_energy_compensate = Ws;
        break;
    default:
        break;
    }
}

void dohome_hal_pwr_measure_reset_power_energy(void){

    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        _power_energy_compensate = 0;
        HLW8012_resetEnergy();
        break;
    default:
        break;
    }
}

DOHOME_UINT16_T dohome_hal_pwr_measure_get_apparent_power_VA(void){

    DOHOME_UINT16_T apparent_power_VA = 0;
    
    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        apparent_power_VA = HLW8012_getApparentPower();
        break;
    default:
        break;
    }

    return apparent_power_VA;
}

DOHOME_INT16_T dohome_hal_pwr_measure_get_get_power_factor(void){
        
    DOHOME_INT16_T power_factor = 0;
    
    switch (_pwr_measure_type)
    {
    case DOHOME_PWR_MEASURE_TYPE_HLW8012:
    case DOHOME_PWR_MEASURE_TYPE_BL0937:
        power_factor = (DOHOME_INT16_T) (100 * HLW8012_getPowerFactor());
        break;
    default:
        break;
    }

    return power_factor;
}

void dohome_hal_pwr_measure_correct_init(void){
    
    _correcting = 1;
     
    switch (_model){
    case 1:
        _power_multiplier =   (  50850000.0 * _vref * _vref * _voltage_resistor / _current_resistor / 48.0 / F_OSC_BL0) / 1.1371681416f;  //15102450
        _voltage_multiplier = ( 221380000.0 * _vref * _voltage_resistor /  2.0 / F_OSC_BL0) / 1.0474137931f; //221384120,171674
        _current_multiplier = ( 531500000.0 * _vref / _current_resistor / 24.0 / F_OSC_BL0) / 1.166666f; // 
        break;
    default:
        _power_multiplier =   ( 1000000.0 * 128 * _vref * _vref * _voltage_resistor / _current_resistor / 48.0 / F_OSC_HLW );
        _voltage_multiplier = ( 1000000.0 * 512 * _vref * _voltage_resistor /  2.0 / F_OSC_HLW );
        _current_multiplier = ( 1000000.0 * 512 * _vref / _current_resistor / 24.0 / F_OSC_HLW );
        break;
    }

}

void dohome_hal_pwr_measure_correct_for_expected(DOHOME_UINT16_T voltage, DOHOME_UINT16_T current, DOHOME_UINT16_T power){
    
    doit_measure_multiplier_cal multiplier;
    if (voltage != 0){
        HLW8012_expectedVoltage(voltage);
    }
    float f_current = (float)(current);
    f_current = f_current/1000.0;
    if (current != 0){
        HLW8012_expectedCurrent(f_current);
    }
    if (power != 0){
        HLW8012_expectedActivePower(power);
    }
    
    multiplier.voltage = _voltage_multiplier;
    multiplier.current = _current_multiplier;
    multiplier.power = _power_multiplier;
    
    printf("multiplier_cal set C:%f V:%f P:%f\n", _current_multiplier, _voltage_multiplier, _power_multiplier);

    dohome_kvs_set_env_blob("dh_measure_mp", &multiplier, sizeof(multiplier));

    _correcting = 0;
}